import { b, BufferContainer, Bytes } from "./Buff";
import * as fs from "fs"

let r = new Int16Array(8);
let mem = new Int16Array(35536)

let offset6 = 0, imm5 = 0, imm9 = 0;
let pcoffset9 = 0, pcoffset11 = 0;
let regsave1 = 0, regsave2 = 0;

let ir = 0, pc = 0, lr = 0;
let opcode = 0, code = 0;
let dr = 0, sr = 0, baser = 0, sr1 = 0, sr2 = 0;
let bit5 = 0, bit11 = 0;
let trapvec = 0, eopcode = 0;
let n = 0, z = 0, c = 0, v = 0;
let ldData = 0;
let letter = "";
// 指令打印
let debug = 1;

function setnz(r: number) {
    n = z = 0;
    if (r < 0) {
        n = 1
    } else if (r == 0) {
        z = 1
    }
}

function setcv(sum: number, x: number, y: number) {
    v = c = 0

    if (x >= 0 && y >= 0) {
        c = 0
    } else if (x < 0 && y < 0) {
        c = 1
    } else if (sum >= 0) {
        c = 1
    } else {
        c = 0
    }

    if ((x < 0 && y >= 0) || (x >= 0 && y < 0)) {
        v = 0
    } else if ((sum < 0 && y >= 0) || (sum >= 0 && x < 0)) {
        v = 1
    } else {
        v = 0
    }
}

function timePrint(msg: string = "") {
    console.log(msg + "-->", new Date().toString())
}
function p(...msg: any[]) {
    console.log(msg)
}
function toChar(num: number) {
    if ((num > toNum('a') && num < toNum("z"))
        || (num > toNum('A') && num < toNum('Z'))
    ) {
        return String.fromCharCode(num)
    } else {
        throw new Error(`toChar错误${num}`)
    }
}
function toNum(c: string) {
    if (c.length > 1 || c.length == 0) {
        throw new Error(`toNum参数错误${c}`)
    }
    return c.charCodeAt(0);
}

function to16String(num: number) {
    let r = num.toString(2);
    while (r.length < 16) {
        r = "0" + r;
    }
    return r;
}
function d(...msg: any[]) {
    if (debug) {
        console.log(
            "pc " + (pc - 2), "opcode " + opcode,
            "ir", ir.toString(16), to16String(ir),
            msg
        )
    }
}
function main(inFile: string) {
    if (!fs.existsSync(inFile)) {
        throw new Error(`文件${inFile}不存在`)
    }
    let fileBuffer = fs.readFileSync(inFile)
    let buffer = new BufferContainer(fileBuffer)
    timePrint("start");

    let first = buffer.readI8(0);
    let second = buffer.readI8(1);

    if (first != toNum('o') || second != toNum('C')) {
        p("文件头错误", `first:${first},${second}`)
    }

    // console.log("mem",mem);
    // return;
    // 跳过第一个Int16
    pc = 1;
    let maxPc = buffer.length / 2;
    // console.log("maxPc",maxPc)

    while (1) {
        ir = buffer.readU16(pc);
        // 偏移指令 pc-1
        pc = pc + 1;

        // 获取内容
        opcode = Bytes.readBytes(ir, 16 - 4, 4);
        pcoffset11 = Bytes.readBytes(ir, 0, 11, false);
        pcoffset9 = imm9 = Bytes.readBytes(ir, 0, 9, false);
        offset6 = Bytes.readBytes(ir, 0, 6, false)
        imm5 = Bytes.readBytes(ir, 0, 5, false);
        trapvec = eopcode = Bytes.readBytes(ir, 0, 5);
        code = dr = sr = Bytes.readBytes(ir, 9, 3);
        sr1 = baser = Bytes.readBytes(ir, 6, 3)
        sr2 = Bytes.readBytes(ir, 0, 3)
        bit5 = Bytes.readBytes(ir, 5, 1);
        bit11 = Bytes.readBytes(ir, 11, 1);


        // break
        // d(pc, opcode, ir, ir.toString(16), to16String(ir))

        switch (opcode) {
            case 0: //br
                // p("br", code, pcoffset9 ,pc, ir, ir.toString(16), to16String(ir))
                if (pcoffset9 == 0) {
                    //数据
                    continue;
                }
                switch (code) {
                    case 0: //brz
                        d(`brz ${pcoffset9}`)

                        if (z == 1) {
                            pc = pc + pcoffset9
                        }
                        break
                    case 1: //brnz
                        d(`brnz ${pcoffset9}`)

                        if (z == 0) {
                            pc = pc + pcoffset9
                        }
                        break
                    case 2: //brn
                        d(`brn ${pcoffset9}`)

                        if (n == 1) {
                            pc = pc + pcoffset9
                        }
                        break
                    case 3: //brp
                        d(`brp ${pcoffset9}`)

                        if (n == z) {
                            pc = pc + pcoffset9
                        }
                        break
                    case 4: //brlt
                        d(`brlt ${pcoffset9}`)

                        if (n != v) {
                            pc = pc + pcoffset9
                        }
                        break
                    case 5: //brgt
                        d(`brgt ${pcoffset9}`)

                        if (n == v && z == 0) {
                            pc = pc + pcoffset9
                        }
                        break
                    case 6: //brc
                        d(`brc ${pcoffset9}`)

                        if (c == 1) {
                            pc = pc + pcoffset9
                        }
                        break
                    case 7: //br
                        d(`br ${pcoffset9}`)

                        pc = pc + pcoffset9
                        break
                }
                break
            case 1: //add
                // p("add",pc, dr,sr1,bit5,sr2,imm5, ir, ir.toString(16), to16String(ir))
                if (bit5 == 1) {
                    d(`add r${dr},r${sr1},${imm5}`)

                    regsave1 = r[sr1];
                    r[dr] = regsave1 + imm5;
                    setcv(r[dr], regsave1, imm5)
                } else {
                    d(`add r${dr},r${sr1},r${sr2}`)

                    regsave1 = r[sr1]
                    regsave2 = r[sr2]
                    r[dr] = regsave1 + regsave2
                    setcv(r[dr], regsave1, regsave2)
                }
                setnz(r[dr])
                break
            case 2: // ld
                d(`ld r${dr},${pcoffset9}`)

                ldData = buffer.readI16(pc + pcoffset9);
                console.log("ld",pc+pcoffset9,dr,r)
                r[dr] = ldData;
                break
            case 3: //st
                d(`st r${sr},${pcoffset9}`)
                // mem[pc + pcoffset9] = r[sr];
                buffer.setI16(pc + pcoffset9, r[sr]);
                console.log("st",  pc + pcoffset9, sr, r,buffer.readI16(pc + pcoffset9))
                break
            case 4: //bl blr
                // console.log("bl blr",bit11)
                if (bit11 == 1) {
                    d(`bl ${pcoffset11}`)
                    // r[7] = lr
                    r[7] = pc; lr = pc;
                    pc = pc + pcoffset11
                } else {
                    d(`blr r${baser}`)
                    // r[7] = lr
                    r[7] = pc; lr = pc;
                    pc = r[baser] + offset6
                }
                break
            case 5: //and
                if (bit5 == 1) {
                    d(`and r${dr},r${sr1},${imm5}`)

                    regsave1 = r[sr1]
                    r[dr] = regsave1 & imm5
                } else {
                    d(`and r${dr},r${sr1},r${sr2}`)

                    regsave1 = r[sr1]
                    regsave2 = r[sr2]
                    r[dr] = regsave1 & regsave2;
                }
                setnz(r[dr])
                break
            case 6: //ldr
                d(`ldr r${dr},r${baser},${offset6}}`)
                // p("ldr baser",r,baser,r[baser])
                ldData = buffer.readI16(r[baser] + offset6);
                // console.log("ldr",ldData)
                r[dr] = ldData;// mem[baser + offset6]
                break
            case 7: //str
                d(`str r${sr},r${baser},${offset6}}`)

                mem[baser + offset6] = r[sr]
                break
            case 8: //cmp
                if (bit5 == 1) {

                } else {

                }
                break
            case 9: //not
                d(`not r${dr},r${sr1}`)

                r[dr] = ~r[sr1]
                setnz(r[dr])
                break
            case 10:
                break
            case 11:
                break
            case 12: //jmp ret
                // lr = r[7] = baser
                if (baser == 7) {
                    d(`ret r${baser}`)
                } else {
                    d(`jmp r${baser}`)
                }
                pc = r[baser] + offset6;
                // console.log("jmp", r, baser, pc)
                break
            case 13:
                break
            case 14: //lea
                d(`lea r${dr},${pcoffset9}`)

                r[dr] = pc + pcoffset9
                break;
            case 15:
                if (trapvec == 0x00) { //halt
                    d(`halt`)
                    return;
                } else if (trapvec == 0x01) { //nl
                    d(`nl`)
                    if (!debug) {
                        console.log("")
                    }
                } else if (trapvec == 0x02) {
                    d(`dout`)
                    if (!debug) {
                        console.log(r[sr])
                    }
                }
                break
        }
        if (pc < 0) {
            console.log("<0", pc)
            break;
        }
        if (pc > maxPc) {
            break
        }
    }
}

function i1() {
    main("test.e");
}

export {
    i1
}